#include "dnsimpl.h"
#include "protocol/dns.h"

CDnsImpl::CDnsImpl()
{

}
CDnsImpl::~CDnsImpl(void)
{
	logi("CDnsImpl::~CDnsImpl");
}
std_method_impl CDnsImpl::init_class(AsynFrameConstPtr AsynFrame, NetConstPtr Net)
{
	HRESULT hr = S_OK;

	hr = AsynFrame->CreateIo(io_udp_socket, (IBase**)&m_pISocket);
	rc_assert(hr == S_OK, E_FAIL)

	hr = AsynFrame->CreateIo(io_operation, (IBase**)&m_pIOperation);
	rc_assert(hr == S_OK, E_FAIL)

	return S_OK;
}
std_method_impl CDnsImpl::Send(LPCSTR hostname, IBase* pProc)
{
	HRESULT hr = S_OK;
	logi("SendUrl ==>%s", hostname);
	dns_t query;
	memset(&query, 0, sizeof(query));
	query.hdr.transaction_id = getpid();
	query.hdr.qr = DNS_QUERY;
	query.hdr.rd = 1;
	query.hdr.nquestion = 1;

	dns_rr_t question;
	memset(&question, 0, sizeof(question));
	strncpy(question.name, hostname, sizeof(question.name));
	question.rtype = DNS_TYPE_A;
	question.rclass = DNS_CLASS_IN;

	query.questions = &question;

	char buf[1024] = { 0x00 };
	int buflen = sizeof(buf);
	buflen = dns_pack(&query, buf, buflen);

	if (buflen < 0) {
		return buflen;
	}

	BasePtr	pIBase;
	hr = this->QueryInterface(IID_IBase, (void**)&pIBase);
	rc_assert(hr == S_OK, E_FAIL)


	hr = m_pIOperation->BindEvent(pIBase);
	rc_assert(hr == S_OK, E_FAIL)

	hr = m_pIOperation->Attach((BUFFER_PTR)buf, 0, buflen);
	rc_assert(hr == S_OK, E_FAIL)

	hr = m_pISocket->CreateSock();
	rc_assert(hr == S_OK, E_FAIL)

	hr = m_pISocket->OpenIo();
	rc_assert(hr == S_OK, E_FAIL)

	hr = m_pISocket->BindIo();
	rc_assert(hr == S_OK, E_FAIL)

	BUFFER_SIZE len = 0;
	len = m_pISocket->SyncSend("114.114.114.114", 53, (BUFFER_PTR)buf, buflen);
	rc_assert(len != 0, E_FAIL)

	hr = m_pIOperation->ClearPtr();
	rc_assert(hr == S_OK, E_FAIL)

	hr = m_pIOperation->GetLen(&len);
	rc_assert(hr == S_OK, E_FAIL)

	hr = m_pIOperation->SetIoParam(0, len);
	rc_assert(hr == S_OK, E_FAIL)

	hr = m_pISocket->ReadIo("114.114.114.114", 53, m_pIOperation, 0);
	rc_assert(hr == S_OK, E_FAIL)

	m_sem.Wait();

	return S_OK;
}

std_method_impl CDnsImpl::Post(LPCSTR hostname, IBase* pProc)
{
	return S_OK;
}
std_method_impl CDnsImpl::Close()
{
	HRESULT hr = S_OK;

	hr = m_pISocket->CloseIo(CLOSE_BOTH_IO);
	rc_assert(hr == S_OK, E_FAIL)

	hr = m_pISocket->CloseSock();
	rc_assert(hr == S_OK, E_FAIL)

	hr = m_pIOperation->UnBindEvent();
	rc_assert(hr == S_OK, E_FAIL)

	m_pISocket.dispose();

	m_pIOperation.dispose();

	return S_OK;
}

std_method_impl CDnsImpl::OnComplete(IBase *pOperation, event_id event, ULONG error_code)
{
	char ip[16] = { 0x00 };
	int ret = 0;
	uint32_t addrs[32] = { 0x00 };

	dns_t resp;
	memset(&resp, 0, sizeof(resp));

	OperationPtr pOper;
	pOperation->QueryInterface(IID_IOperation, (void**)&pOper);

	ERROR_CODE error = 0;
	BUFFER_SIZE size = 0;
	pOper->GetResult(&error, &size);

	BUFFER_PTR ptr;

	pOper->GetPtr(&ptr);

	int nparse = dns_unpack((char*)ptr, size, &resp);
	if (nparse != size) {

	}

	dns_rr_t* rr = resp.answers;
	int addr_cnt = 0;
	if (/*resp.hdr.transaction_id != query.hdr.transaction_id ||*/ resp.hdr.qr != DNS_RESPONSE ||
		resp.hdr.rcode != 0) {
		ret = S_ERROR;
		return 0;
	}

	if (resp.hdr.nanswer == 0) {
		ret = 0;
		return 0;
	}

	for (int i = 0; i < resp.hdr.nanswer; ++i, ++rr) {
		char name[256];

		if (rr->rtype == DNS_TYPE_A) {
			if (addr_cnt < 16 && rr->datalen == 4) {
				memcpy(addrs + addr_cnt, rr->data, 4);
			}
			++addr_cnt;
		}
		else if (rr->rtype == DNS_TYPE_CNAME) {

			dns_name_decode(rr->data, name);
		}

	}
	ret = addr_cnt;

	for (int i = 0; i < ret; ++i) {
		_inet_ntop(AF_INET, (void*)&addrs[i], ip, 16);
		logi("ip==>[%s]", ip);

	}
	dns_free(&resp);

	m_sem.Post();
	return S_OK;
}




